{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "77a24060-17cc-4526-be70-1d165cd8db21",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<torch._C.Generator at 0x22cc5c07dd0>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "torch.manual_seed(1024)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c55bbb22-2b8f-42a4-9865-cba76a85eaf7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义线性模型和Sigmoid函数\n",
    "class Linear:\n",
    "    # input :(B, in_features)\n",
    "    # output (B, out_features)\n",
    "\n",
    "    def __init__(self,in_features,out_features,bias=False):\n",
    "        self.weight = torch.randn(in_features,out_features,requires_grad = True)\n",
    "        if bias:\n",
    "            self.bias = torch.randn(out_features,requires_grad = True)\n",
    "        else :\n",
    "            self.bias = None\n",
    "\n",
    "    def __call__(self,x):\n",
    "        # x :(B, in_features)\n",
    "        #self.weight (in_features, out_features)\n",
    "        self.out = x @ self.weight \n",
    "        if self.bias is not None:\n",
    "            self.out += self.bias\n",
    "        return self.out\n",
    "\n",
    "    def parameters(self):\n",
    "        # 返回模型参数\n",
    "        if self.bias is not None:\n",
    "            return [self.weight,self.bias]\n",
    "        return [self.weight]\n",
    "\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "cab9420c-8a1a-414b-b0d8-23527237f155",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 0.8725, -1.3424,  1.7336,  0.3008],\n",
       "        [ 1.2790, -0.4374,  1.9408,  0.5689],\n",
       "        [-0.8642, -0.0465, -0.7873,  0.2805],\n",
       "        [ 4.1653, -1.4934,  5.5663,  0.4573],\n",
       "        [-1.5655, -0.3404, -1.8207, -0.3939]], grad_fn=<MmBackward0>)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mod = Linear(3,4)\n",
    "x = torch.randn(5,3)\n",
    "mod(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "603baf53-e141-4087-b662-8a4a3dafdaac",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Sigmoid:\n",
    "\n",
    "    def __call__(self,x):\n",
    "        self.out = torch.sigmoid(x)\n",
    "        return self.out\n",
    "    def parameters(self):\n",
    "        return []\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0c051e55-315c-47ab-b9c8-6c29b9f9c589",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[0.7731, 0.3853],\n",
       "        [0.2635, 0.2614],\n",
       "        [0.5259, 0.6454]])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = Sigmoid()\n",
    "x = torch.randn(3,2)\n",
    "s(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "633f56f0-5659-4762-b2cd-da2a667fb45b",
   "metadata": {},
   "source": [
    "## 将Sigmoid激活层和Linear线性层放到一个Perception层中"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "0bde412e-4dd9-468a-be49-3483d7d697f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Perception:\n",
    "    def __init__(self,in_features):\n",
    "        self.ln = Linear(in_features,1)\n",
    "        self.f = Sigmoid()\n",
    "\n",
    "    def __call__(self,x):\n",
    "        # x (B ,in_features)\n",
    "        self.out = self.f(self.ln(x)) # (B,1)\n",
    "        return self.out\n",
    "\n",
    "    def parameters(self):\n",
    "        return self.ln.parameters() + self.f.parameters()\n",
    "\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "42066667-b08e-4349-9a68-7778bdc9ba88",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[0.2832],\n",
       "        [0.6279],\n",
       "        [0.9842],\n",
       "        [0.5553],\n",
       "        [0.2975]], grad_fn=<SigmoidBackward0>)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p = Perception(3)\n",
    "x = torch.randn(5,3)\n",
    "p(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3052f946-75ec-4cbc-81a3-8cdb5b9babf3",
   "metadata": {},
   "source": [
    "## 对于上面的Perception类进行修改,让其变成二分类问题"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "63353f53-39c0-43bf-b41f-c2bea2d60ac5",
   "metadata": {},
   "outputs": [],
   "source": [
    "class LogitRegression:\n",
    "    # input : (B, in_features)\n",
    "    # output :(B, 2)\n",
    "    def __init__(self,in_features):\n",
    "        self.pos = Linear(in_features,1)\n",
    "        self.neg = Linear(in_features,1)\n",
    "\n",
    "    def __call__(self,x):\n",
    "        # x (B ,in_features)\n",
    "        self.out = torch.concat((self.pos(x),self.neg(x)),dim = -1) # (B,2)\n",
    "        return self.out\n",
    "    def parameters(self):\n",
    "        return self.pos.parameters() + self.neg.parameters()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "07bc3ead-49fc-4177-a26b-72d7067828b0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 0.2416,  2.1290],\n",
      "        [ 0.4265, -0.0501],\n",
      "        [-0.0068, -2.6255],\n",
      "        [-0.4713, -3.3793],\n",
      "        [ 0.1082,  1.3834],\n",
      "        [ 0.3758,  0.3488],\n",
      "        [ 0.2263, -1.2600],\n",
      "        [ 0.6805,  1.0719],\n",
      "        [-0.2840,  0.2226],\n",
      "        [-1.4588, -3.7929],\n",
      "        [-0.7084, -2.3946],\n",
      "        [ 1.6162,  1.5433]], grad_fn=<CatBackward0>)\n"
     ]
    }
   ],
   "source": [
    "lr = LogitRegression(3)\n",
    "x = torch.randn(12,3)\n",
    "logits = lr(x)\n",
    "print(logits)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9bf416d2-27e9-42a6-8751-2b8d14c011a0",
   "metadata": {},
   "source": [
    "## 将二维的输入经过softmax变换后输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "4b2d5cda-e5d8-4ce7-abda-4d4a4dda6c29",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[0.1315, 0.8685],\n",
      "        [0.6169, 0.3831],\n",
      "        [0.9321, 0.0679],\n",
      "        [0.9482, 0.0518],\n",
      "        [0.2184, 0.7816],\n",
      "        [0.5067, 0.4933],\n",
      "        [0.8155, 0.1845],\n",
      "        [0.4034, 0.5966],\n",
      "        [0.3760, 0.6240],\n",
      "        [0.9117, 0.0883],\n",
      "        [0.8437, 0.1563],\n",
      "        [0.5182, 0.4818]], grad_fn=<SoftmaxBackward0>)\n"
     ]
    }
   ],
   "source": [
    "import torch.nn.functional as F\n",
    "probs = F.softmax(logits,dim = -1)\n",
    "print(probs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47219421-b655-48ec-b3cb-e8c2ec864f63",
   "metadata": {},
   "source": [
    "## 从logits 经过softmax变换到 probs ,再取出每一行的最大值的索引,也就是预测值pred"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "3b8582ca-1dc9-4750-9019-4672ac694a62",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0])\n"
     ]
    }
   ],
   "source": [
    "pred = torch.argmax(probs,dim = -1)\n",
    "print(pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a8c33f0-c352-41d4-9022-7f1bb5b33ddc",
   "metadata": {},
   "source": [
    "## torch.nn.functional.cross_entropy的使用\n",
    "(input,target) \n",
    "- 其中 input 是logits (B ,out_features)\n",
    "- target 是预测结果,  (B,1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "f79c4df2-0e08-4408-b08b-bbaafcd284dc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.31544992327690125\n"
     ]
    }
   ],
   "source": [
    "loss = F.cross_entropy(logits,pred)\n",
    "print(loss.item())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61a16d1a-40bb-4005-9df4-06e06bb8ba5c",
   "metadata": {},
   "source": [
    "## 使用自己编写的cross_entropy函数进行求解"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "debf2206-70c3-4bf6-a2c3-927a4afeedb3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.31544995307922363"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "-probs.log()[range(len(pred)),pred].mean().item()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "45e17ce1-70e3-46ad-9a0f-3e9729e7050a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import make_blobs\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "51c1a58a-d1b3-46fd-89eb-2788ed8b2cdb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.collections.PathCollection at 0x22ccdf61950>"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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5z4nV1xxhVx//fWWzrz9VCqVpu8dx5lyH7L11a9+Vf7jP/5DNm/ebPtU2tbIyIisXr1aNm3aJCeeeKLt00kFOSKGHTp0SA4dOhR6zLx582T58uXyk5/8pOl/tPV6XTo6OuSaa66Rxx9/3PSptgXV6+H9Ax8bG5MlS5bIBRdcII899phMm0bsbtqRI0fk5JNPlqeeekouu+yyY/evXr1aXnnlFXnxxRctnl37uummm+SZZ56Rf/u3f5P58+fbPp229cwzz8hll10mHR0dx+6r1+tSqVRk2rRpMjk52fSzIiAQyYm9e/fKxMTEse/Hxsbks5/9rDz99NNywQUXSLVatXh27Wl0dFSWLFki5557rmzatKlw/7iL7IILLpBzzz1XHnrooWP3nX322dLb20uyasYcx5GbbrpJfvSjH8nw8LAsWLDA9im1tffee0/+53/+p+m+a6+9Vj72sY/JN7/5zUIumZEjkhOnn3560/cf/vCHRUTkzDPPJAixYGxsTBYvXiynn3663HPPPXLw4MFjP/voRz9q8czawy233CJf/vKX5bzzzpNPfvKTsnHjRtm7d6+sWrXK9qm1nRtvvFEGBgZk27ZtMmPGjGN5Ol1dXXLSSSdZPrv2M2PGjCnBximnnCI9PT2FDEJECEQAX88995y88cYb8sYbb0wJBJlENG/ZsmVy+PBh+c53viP79++Xc845R37605/KGWecYfvU2o5XQr148eKm+x999FFZsWJF9ieE0mFpBgAAWEPmHQAAsIZABAAAWEMgAgAArCEQAQAA1hCIAAAAawhEAACANQQiAADAGgIRAABgDYEIAACwhkAEAABYQyACAACsIRABAADW/H8uUYinclRh+AAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "data =make_blobs(200,centers=[[-2,-2],[2,2]])\n",
    "x,y=data\n",
    "plt.scatter(x[y==0,0],x[y==0,1],color=\"red\")\n",
    "plt.scatter(x[y==1,0],x[y==1,1],color=\"blue\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "358ade22-c315-4b36-96cf-420b34c3efd1",
   "metadata": {},
   "outputs": [],
   "source": [
    "batch_size = 20\n",
    "max_steps = 2000\n",
    "lr = 0.01\n",
    "x,y = torch.tensor(data[0]).float(), torch.tensor(data[1]).long()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "15146611-30c2-452a-b1f1-e6e1531ee666",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第0轮损失0.23735523223876953\n",
      "第100轮损失0.08800993114709854\n",
      "第200轮损失0.058868974447250366\n",
      "第300轮损失0.04559507593512535\n",
      "第400轮损失0.03778038173913956\n",
      "第500轮损失0.03254734352231026\n",
      "第600轮损失0.0287587009370327\n",
      "第700轮损失0.025868099182844162\n",
      "第800轮损失0.023577887564897537\n",
      "第900轮损失0.02171100676059723\n",
      "第1000轮损失0.020155057311058044\n",
      "第1100轮损失0.018834872171282768\n",
      "第1200轮损失0.017698239535093307\n",
      "第1300轮损失0.016707580536603928\n",
      "第1400轮损失0.015835188329219818\n",
      "第1500轮损失0.0150600615888834\n",
      "第1600轮损失0.014366015791893005\n",
      "第1700轮损失0.013740306720137596\n",
      "第1800轮损失0.013172900304198265\n",
      "第1900轮损失0.012655568309128284\n"
     ]
    }
   ],
   "source": [
    "model = LogitRegression(2)\n",
    "lossi = []\n",
    "for t in range(max_steps):\n",
    "    ix = (t * batch_size) % len(y)\n",
    "    xx = x[ix:ix+batch_size]\n",
    "    yy = y[ix:ix+batch_size]\n",
    "    logits = model(xx)\n",
    "    loss = F.cross_entropy(logits,yy)\n",
    "    loss.backward()\n",
    "    with torch.no_grad():\n",
    "        for p in model.parameters():\n",
    "            p -= lr * p.grad\n",
    "            p.grad = torch.zeros(p.shape)\n",
    "    if t % 100 == 0:\n",
    "        print(f'第{t}轮损失{loss.item()}')\n",
    "    lossi.append(loss.item())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "0a8cf8b5-7a64-4d92-b8e4-0bef906b2482",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x22cce1bdb90>]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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oGXGv2/fPkVHRys+Pl633367Dh48KEnKyspSQUFBg9r9/f119dVXO2v3hPGdUV1drXfeeUe/+MUvGtzY09P33/e5a5+lpaXJbrdr+PDhzj6JiYmy2+2WG3dpaalsNps6derUoP3dd99VWFiYBgwYoN/+9rcN/s/W6uO70M+k1cf3fUePHtXKlSt17733NnrOU/bhD38brP49bDM3P3S3oqIi1dXVKSIiokF7RESECgoKTKrKdYZhaObMmbrqqqs0cOBAZ/v48eP1k5/8RHFxccrKytKTTz6pMWPGKD09Xf7+/iooKJCfn586d+7cYHtWGf/w4cO1ePFi9enTR0ePHtUf//hHjRgxQjt37nTWd7Z9d/jwYUmy/Pi+78MPP1RJSYnuueceZ5un778fctc+KygoUHh4eKPth4eHW2rclZWVeuyxx3THHXc0uJHcnXfeqfj4eEVGRurbb7/VrFmztH37duchQSuPzx2fSSuP74f+9re/KTg4WLfeemuDdk/Zh2f7bbD695DA0oTv/x+tdHon/7DNyh566CF988032rRpU4P2KVOmOP88cOBADRs2THFxcVq5cmWjL+D3WWX848ePd/550KBBSkpKUq9evfS3v/3NeaJfS/adVcb3fQsXLtT48eMVHR3tbPP0/Xcu7thnZ+tvpXHX1NTo9ttvV319vebPn9/gufvvv9/554EDB6p3794aNmyYtm3bpqFDh0qy7vjc9Zm06vh+6K233tKdd96pgICABu2esg/P9dsgWfd7yCGhcwgLC5O3t3ejNFhYWNgofVrVww8/rBUrVmjdunWKiYk5b9+oqCjFxcVp3759kqTIyEhVV1frxIkTDfpZdfxBQUEaNGiQ9u3b57xa6Hz7zlPGd/jwYa1du1b33Xffeft5+v5z1z6LjIzU0aNHG23/2LFjlhh3TU2NJk+erKysLKWmpjaYXTmboUOHytfXt8F+tfL4vq8ln0lPGd/GjRu1Z8+eJr+XkjX34bl+G6z+PSSwnIOfn58SEhKc03hnpKamasSIESZV1TyGYeihhx7S8uXL9dlnnyk+Pr7J1xQXFysnJ0dRUVGSpISEBPn6+jYYf35+vr799ltLjr+qqkq7d+9WVFSUczr2+7VXV1dr/fr1zto9ZXxvv/22wsPDNWHChPP28/T95659lpSUpNLSUn355ZfOPlu3blVpaanp4z4TVvbt26e1a9eqS5cuTb5m586dqqmpce5XK4/vh1rymfSU8S1cuFAJCQkaPHhwk32ttA+b+m2w/PewxafrtgNLly41fH19jYULFxq7du0yZsyYYQQFBRmHDh0yu7Tz+uUvf2nY7Xbj888/N/Lz852PkydPGoZhGGVlZcZvfvMbY/PmzUZWVpaxbt06IykpyejWrZvhcDic25k2bZoRExNjrF271ti2bZsxZswYY/DgwUZtba1ZQ3P6zW9+Y3z++efGwYMHjS1bthgTJ040goODnfvm+eefN+x2u7F8+XJjx44dxk9/+lMjKirKY8ZnGIZRV1dndO/e3Xj00UcbtHvq/isrKzMyMjKMjIwMQ5Lx0ksvGRkZGc6rZNy1z2644QbjsssuM9LS0oy0tDRj0KBBxsSJE00dX01NjXHzzTcbMTExRmZmZoPvZVVVlWEYhrF//37jmWeeMb766isjKyvLWLlypdG3b19jyJAhlh+fOz+TZo2vqTGeUVpaanTo0MF47bXXGr3e6vuwqd8Gw7D295DA0oR58+YZcXFxhp+fnzF06NAGlwZblaSzPt5++23DMAzj5MmTRnJystG1a1fD19fX6N69u3H33Xcb2dnZDbZz6tQp46GHHjJCQ0ONwMBAY+LEiY36mGXKlClGVFSU4evra0RHRxu33nqrsXPnTufz9fX1xlNPPWVERkYa/v7+xujRo40dO3Y02IaVx2cYhrF69WpDkrFnz54G7Z66/9atW3fWz+Xdd99tGIb79llxcbFx5513GsHBwUZwcLBx5513GidOnDB1fFlZWef8Xq5bt84wDMPIzs42Ro8ebYSGhhp+fn5Gr169jF//+tdGcXGx5cfnzs+kWeNraoxnvPHGG0ZgYKBRUlLS6PVW34dN/TYYhrW/h7b/DAIAAMCyOIcFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABYHoEFAABY3v8Pyf7QrG59FvMAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(lossi)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d2e617d3-951d-4d74-99ef-46943eca660b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "class Linear:\n",
    "    def __init__(self, in_features, out_features, bias=False):\n",
    "        # 初始化权重和偏置\n",
    "        self.weight = np.random.randn(in_features, out_features)\n",
    "        if bias:\n",
    "            self.bias = np.zeros(out_features)\n",
    "        else:\n",
    "            self.bias = None\n",
    "\n",
    "    def __call__(self, x):\n",
    "        # 执行线性变换\n",
    "        self.out = np.dot(x, self.weight)\n",
    "        if self.bias is not None:\n",
    "            self.out += self.bias\n",
    "        return self.out\n",
    "\n",
    "    def parameters(self):\n",
    "        # 返回模型的参数列表\n",
    "        if self.bias is not None:\n",
    "            return [self.weight, self.bias]\n",
    "        else:\n",
    "            return [self.weight]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "acc5913e-19b7-4442-919c-823670530458",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
